Skip to content

feat(standards): pay the transaction fee in guarded and smart multisig auth - #3798

Open
mmagician wants to merge 12 commits into
mmagician-claude/port-fee-assert-boundfrom
mmagician-claude/port-multisig-pays-fee
Open

feat(standards): pay the transaction fee in guarded and smart multisig auth#3798
mmagician wants to merge 12 commits into
mmagician-claude/port-fee-assert-boundfrom
mmagician-claude/port-multisig-pays-fee

Conversation

@mmagician

@mmagician mmagician commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Port of #3786 to next.

On next the guarded and smart multisig components decode their auth args but drop the conversion info and pay no fee. Both paths can authenticate below the account's spending quorum (guardian key rotation, or a procedure with a reduced per-procedure threshold), and the conversion rate is host-supplied, so paying the fee unbounded would let such an authorization move arbitrary value out of the vault as a fee note. This wires in the bounded flow from #3797 instead.

Notes

Differs from the rc version in following next's auth-args scheme: the conversion info comes from resolve_auth_args rather than load_conversion_info, so the auth args no longer double as the summary salt, and the summary is bound to the block number as on next.

Comment thread crates/miden-standards/asm/components/auth/guarded_multisig/guarded_multisig.masm Outdated
Comment thread crates/miden-standards/asm/components/auth/guarded_multisig/guarded_multisig.masm Outdated
Comment thread crates/miden-standards/asm/components/auth/multisig_smart/multisig_smart.masm Outdated
…g auth

Port of #3786 to next. Both components run estimate_fee, the sponsorship
payment, resolve_payment_info, assert_fee_bound (2/1, native fee asset) and
pay_estimated_fee between multisig::resolve_auth_args and the summary, and
pass the number of notes the payment created to guardian::verify_signature
and multisig_smart::auth_tx, whose note restrictions exclude them.
tx_policy::assert_no_output_notes reads the verified count itself.

Adaptations to next: the conversion info comes from resolve_auth_args
(MultisigAuthArgs) rather than load_conversion_info, so the auth args no
longer double as the summary salt; multisig_smart::auth_tx takes
(num_own_output_notes, block_number, SALT); the fee asset is read via
tx::get_fee_asset_id; the sponsorship wrapper dropped in the fee-split port
is spelled out at the call sites; pay_fee reads the fee asset once via an
estimate_fee_for_asset helper. Tests build MultisigAuthArgs, share the
multisig fixture and signing helpers, and drop the salt-binding assertions.
Cost tables and bench-tx.json regenerated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
…tisig auth

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
…sig auth

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
…g auth

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
…ig components

Pull the fee-paying block of the guarded and smart multisig auth procedures
into multisig::pay_bounded_fee, which takes the number of signers and the
conversion info and returns the number of notes the payment created. The
fee bound constants and the drain rationale live there once; the components
keep only the signer count that differs. Written against fee::estimate_fee
taking the fee asset ID, so the fee asset is read once for the estimate and
the sponsorship notes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
The estimate records every network note's sponsorship price in a local
table, which the sponsorship payment reads back instead of pricing the
notes through FPI a second time, mirroring fee::pay_fee.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
AuthMultisig now pays its fee through multisig::pay_bounded_fee, so the
host-supplied conversion rate can move at most twice the computed fee out
of the vault, in the native fee asset, closing the drain of #3763. The
bound's rationale is reworded to cover every multisig component.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
Composing pay_fee from resolve_payment_info and pay_estimated_fee cost
every plain fee payer 121 auth cycles and pushed the two-P2ID ECDSA
consumption into the next padded-trace bracket. Only the bounded multisig
payment composes the two procedures; pay_fee keeps its inline tail.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
The sponsorship price table now holds one fee asset value word per output
note, so the local table grows to fees::SPONSORSHIP_PRICE_TABLE_NUM_ELEMENTS
as in fee::pay_fee. The pointer plumbing is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkQzFtRjyDwQ7iVtRbsHsn
@mmagician
mmagician force-pushed the mmagician-claude/port-multisig-pays-fee branch from 0611809 to 78fe2df Compare September 3, 2026 15:10
@mmagician
mmagician requested a review from bobbinth September 3, 2026 15:12

@zeapoz zeapoz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

Comment thread CHANGELOG.md

### Features

- [BREAKING] `AuthMultisig` now bounds its fee payment via `fee::assert_fee_bound` to the native fee asset at at most twice the computed fee, closing the fee drain of [#3763](https://github.com/0xMiden/protocol/issues/3763); its code commitment changes.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- [BREAKING] `AuthMultisig` now bounds its fee payment via `fee::assert_fee_bound` to the native fee asset at at most twice the computed fee, closing the fee drain of [#3763](https://github.com/0xMiden/protocol/issues/3763); its code commitment changes.

This is already mentioned in the following entry so no need to explicitly state it separately imo

Comment on lines +80 to +81
#! - num_own_output_notes is the number of output notes the caller itself created, e.g. the notes
#! a `fee::pay_fee` flow creates to pay the transaction fee.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#! - num_own_output_notes is the number of output notes the caller itself created, e.g. the notes
#! a `fee::pay_fee` flow creates to pay the transaction fee.
#! - num_own_output_notes is the number of output notes the caller itself created, e.g. the notes
#! a `multisig::pay_bounded_fee` flow creates to pay the transaction fee.

Is this correct? Maybe I'm missing some detail, but I can't find a flow where fee::pay_fee reaches this proc, but multisig::pay_bounded_fee does

Comment on lines +48 to +53
use super::multisig::{
fee_paying_auth_args,
multisig_auth_estimate,
multisig_fixture,
sign_with_all,
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Thanks for deduplicating this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AuthMultisig: unbounded fee conversion lets approvers drain the vault via the fee note

3 participants